feat(api/v2): ✨ Implement showcase routes - #78
Conversation
|
Nice! I think your currently missing |
Showcases need an image before they can be created, and v2 had no way to store one. This adds the missing half: a thin S3Service over the CDN bucket the v1 API already writes to, and an UploadsService that records what it stored as an Upload row. The blur placeholder is plaiceholder's base64 pipeline reimplemented on sharp, because plaiceholder v3 ships ESM only and api-v2 compiles to CommonJS. Unlike v1, the recorded width and height are the real ones rather than the fixed 1920x1080 that fell out of its resize call. Uploads are shared with claims, so deletion only removes a row nothing else points at. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The routes landed as drafts: nothing checked who was calling them, the service was never registered as a provider, and creating a showcase required an image that could not be uploaded yet. Reads stay public, because showcases are what the website puts on its landing page, and gain the `:teamId` prefix the roadmap asks for, with the same `?slug=true` lookup v1 offers. Writes go through TeamScope, so a team can only touch its own showcases, and `approved` is deliberately not updatable: a team must not be able to approve itself onto the front page. Creating a showcase now takes either a multipart image or an existing uploadId, and deleting one takes its image with it unless a claim or another showcase still uses it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
f7b51c1 to
fc131a1
Compare
|
Picked this up and finished the two TODOs. Rebased onto the current Image upload — v2 had no way to store one, so the create route could never work:
Authorization — reads stay public (showcases are what the landing page renders), writes go through Also fixed two things in the draft: One known gap, deliberately left in: Verified: 160 tests pass (34 new, including a supertest pass over the real router for the public/scoped split), |
Review feedback: the image DTO was missing `checked`, and `include: { image: true }`
returned whatever the Upload table happened to hold, including `claimId`.
Selecting the columns explicitly makes the response and ShowcaseImageDto the
same shape, so the docs cannot drift from what callers actually receive.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
@Nudelsuppe42 caught up on your review from January:
While adding PR description is rewritten and the branch is rebased on current |
Implements the showcase routes from #62.
GET /showcasesGET /:teamId/showcases?slug=trueto look the team up by slugPOST /showcases,POST /:teamId/showcasesPUT /showcases/:id,PUT /:teamId/showcases/:idDELETE /showcases/:id,DELETE /:teamId/showcases/:idReads are public because showcases are what the website puts on its landing page. Writes go through
TeamScope, so a team can only touch its own showcases and a:teamIdprefix has to name the authenticated team.approvedis readable and filterable but deliberately not updatable: a team should not be able to approve itself onto the front page.Creating a showcase takes either a multipart
imageor an existinguploadId, matching the create/link split v1 offers.Image upload
v2 had no way to store an image, so this adds one:
common/db/external/s3.service.ts— a thin wrapper over the same CDN bucket the v1 API writes to. Missing credentials log a warning and fail calls with 503 rather than taking the app down.common/uploads/uploads.service.ts— validates type, size and readability, stores the object, records theUploadrow, and removes the object again if the row write fails. Deleting an upload only happens when no claim and no other showcase still points at it.The blur placeholder is plaiceholder's
base64pipeline reimplemented on sharp, because plaiceholder v3 ships ESM only and api-v2 compiles to CommonJS. The recorded width and height are the real dimensions, unlike v1's fixed 1920x1080 that falls out of its resize call.Known gap
Uploadrows are not team-scoped in the schema, so a team can pass another team'suploadIdand attach its image to their own showcase. v1 has the same hole in its link route. Closing it needs a schema change — an owner column onUpload, or a join back through the showcase or claim that created it — which is out of scope here.Testing
160 tests pass, 34 of them new: unit specs for the controller, the service and
UploadsService, plus a supertest pass over the real router covering the public/scoped split and the:teamIdprefix mismatch.tsc --noEmit,nest buildandeslintare clean apart from the 6 pre-existingunbound-methoderrors in the older specs.Closes #62